﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using SysBitmapImage = System.Windows.Media.Imaging.BitmapImage;
using iNet.Tasks;

namespace iNet.Media.Imaging
{
	/// <summary>
	/// 常見格式的影像解碼工作。
	/// </summary>
	public abstract class BasicImageDecodeTask : ImageDecodeTask
	{
		#region 建構子
		/// <summary>
		/// 初始化新的 WicBasedImageDecodeTask 執行個體。
		/// </summary>
		/// <param name="decodingFormat">影像的解碼格式。</param>
		/// <exception cref="ArgumentNullException">decodingFormat 為 Null (Visual Basic 為 Nothing)。</exception>
		internal BasicImageDecodeTask(IMediaFormat decodingFormat)
			: base(decodingFormat)
		{ }
		/// <summary>
		/// 初始化新的 WicBasedImageDecodeTask 執行個體。
		/// </summary>
		/// <param name="decodingFormat">影像的解碼格式。</param>
		/// <param name="source">影像媒體儲存來源。</param>
		/// <param name="mediaDecodeReqs">媒體解碼請求。</param>
		/// <param name="imageDecodeReqs">影像解碼請求。</param>
		/// <exception cref="ArgumentNullException">decodingFormat 為 Null (Visual Basic 為 Nothing)。</exception>
		internal BasicImageDecodeTask(IMediaFormat decodingFormat, MediaStorage source, MediaDecodeRequests mediaDecodeReqs, ImageDecodeRequests imageDecodeReqs)
			: base(decodingFormat, source, mediaDecodeReqs, imageDecodeReqs)
		{ }
		#endregion
		#region Decode
		protected override bool Decode(TaskVersion version, MediaStorage source, MediaDecodeRequests mediaDecodeReqs, ImageDecodeRequests imageDecodeReqs)
		{
			//鎖定資料流
			Stream stream;
			if (!this.GetMediaStorageStream(version, source, 50, out stream))
				throw new MediaDecodeFailedException(MediaDecodeFailedReason.CannotAccessStream);
			
			//取得資料
			bool autoRotate = this.AutoRotate;
			bool ignoreColorProfile = this.IgnoreColorProfile;
			bool preservePixelFormat = this.PreservePixelFormat;

			//解碼
			try
			{
				//解碼中繼資料
				ImageOrientation? orientation = null;
				IMetadata metadata;
				if ((mediaDecodeReqs & Media.MediaDecodeRequests.DecodeMetadata) != 0)
				{
					metadata = this.DecodeMetadataInternal(stream, true);
					this.SetDecodedMetadata(version, metadata);
					if (autoRotate && metadata is IImageMetadata)
						orientation = (metadata as IImageMetadata).Orientation;
				}
				else
					metadata = null;

				//更新進度
				this.ReportProgress(version, 20);

				//解碼影像方向
				bool rotateImage;
				if (autoRotate)
				{
					if (orientation == null)
						orientation = this.DecodeImageOrientationInternal(version, stream);
					switch (orientation)
					{
						case ImageOrientation.Rotate270:
						case ImageOrientation.Rotate270FlipY:
						case ImageOrientation.Rotate90:
						case ImageOrientation.Rotate90FlipY:
							rotateImage = true;
							break;
						default:
							rotateImage = false;
							break;
					}
				}
				else
					rotateImage = false;

				//中斷確認
				if (this.IsCanceling)
					return true;

				//解碼影像大小
				if ((imageDecodeReqs & Imaging.ImageDecodeRequests.DecodePixelSize) != 0)
					this.SetDecodedPixelSize(version, this.DecodePixelSizeInternal(stream, rotateImage));

				//更新進度
				this.ReportProgress(version, 30);

				//解碼影像
				if ((mediaDecodeReqs & Media.MediaDecodeRequests.DecodeThumbnailImage) != 0 || (imageDecodeReqs & Imaging.ImageDecodeRequests.DecodeImage) != 0)
				{
					if ((imageDecodeReqs & Imaging.ImageDecodeRequests.DecodeImage) != 0)
					{
						try
						{
							if (!this.DoAction(new Action(
								delegate()
								{
									//解碼縮圖
									if ((mediaDecodeReqs & Media.MediaDecodeRequests.DecodeThumbnailImage) != 0)
										this.SetDecodedThumbnailImage(version, this.DecodeThumbnailInternal(stream));

									//解碼影像
									if ((imageDecodeReqs & Imaging.ImageDecodeRequests.DecodeImage) != 0)
									{
										//取得解碼大小
										int? decodeWidth = this.DecodePixelWidth;
										int? decodeHeight = this.DecodePixelHeight;
										if (decodeWidth != null && decodeHeight != null && this.MaintainAspectRatio)
										{
											Size<int> size = ((imageDecodeReqs & Imaging.ImageDecodeRequests.DecodePixelSize) == 0 ? this.DecodePixelSizeInternal(stream, rotateImage) : this.DecodedPixelSize.GetValueOrDefault());
											size = LayoutUtility.GetRatioStretchSize(new Size<int>(decodeWidth.Value, decodeHeight.Value), size, StretchDirection.DownOnly);
											if (!rotateImage)
											{
												decodeWidth = (int)size.Width;
												decodeHeight = (int)size.Height;
											}
											else
											{
												decodeWidth = (int)size.Height;
												decodeHeight = (int)size.Width;
											}
										}

										//解碼影像
										try
										{
											//建立 BitmapSource
											SysBitmapImage sysBmpImage = new SysBitmapImage();

											//設定解碼參數
											sysBmpImage.BeginInit();
											sysBmpImage.StreamSource = stream;
											if (decodeWidth != null)
												sysBmpImage.DecodePixelWidth = decodeWidth.Value;
											if (decodeHeight != null)
												sysBmpImage.DecodePixelHeight = decodeHeight.Value;
											if (ignoreColorProfile)
												sysBmpImage.CreateOptions |= BitmapCreateOptions.IgnoreColorProfile;
											if (preservePixelFormat)
												sysBmpImage.CreateOptions |= BitmapCreateOptions.PreservePixelFormat;
											sysBmpImage.CacheOption = BitmapCacheOption.OnLoad;
											sysBmpImage.EndInit();

											//旋轉影像
											BitmapSource bitmapSource;
											if (autoRotate)
											{
												Transform transform;
												switch (orientation.Value)
												{
													case ImageOrientation.Rotate270:
														transform = new RotateTransform(90);
														break;
													case ImageOrientation.Rotate270FlipY:
														transform = new TransformGroup();
														(transform as TransformGroup).Children.Add(new RotateTransform(90));
														(transform as TransformGroup).Children.Add(new ScaleTransform(1, -1));
														break;
													case ImageOrientation.Rotate90:
														transform = new RotateTransform(270);
														break;
													case ImageOrientation.Rotate90FlipY:
														transform = new TransformGroup();
														(transform as TransformGroup).Children.Add(new RotateTransform(270));
														(transform as TransformGroup).Children.Add(new ScaleTransform(1, -1));
														break;
													case ImageOrientation.Rotate0FlipX:
														transform = new ScaleTransform(-1, 1);
														break;
													case ImageOrientation.Rotate180:
														transform = new RotateTransform(180);
														break;
													case ImageOrientation.Rotate180FlipX:
														transform = new TransformGroup();
														(transform as TransformGroup).Children.Add(new RotateTransform(180));
														(transform as TransformGroup).Children.Add(new ScaleTransform(-1, 1));
														break;
													default:
														transform = null;
														break;
												}
												if (transform != null)
													bitmapSource = new TransformedBitmap(sysBmpImage, transform);
												else
													bitmapSource = sysBmpImage;
											}
											else
												bitmapSource = sysBmpImage;

											//解碼影像
											if ((imageDecodeReqs & Imaging.ImageDecodeRequests.DecodeImage) != 0)
											{
												IImage image = new BitmapImage(source, bitmapSource, true, metadata, this.DecodingFormat);
												this.SetDecodedImage(version, image);
											}
										}
										catch (Exception ex)
										{
											throw new MediaDecodeFailedException(MediaDecodeFailedReason.InvalidFormat, ex);
										}
									}
								}
								), Timeout.Infinite))
								return true;
						}
						catch (TargetInvocationException ex)
						{
							throw ex.InnerException;
						}
					}
				}

				//更新進度
				this.ReportProgress(version, 100);
			}
			finally
			{
				this.ReleaseMediaStorageStream(version, source, stream);
			}

			//解碼完成
			return true;
		}
		#endregion
		#region DecodeImageOrientationInternal
		ImageOrientation DecodeImageOrientationInternal(TaskVersion version, Stream stream)
		{
			long position = stream.Position;
			try
			{
				return this.DecodeImageOrientationOverride(version, stream);
			}
			finally
			{
				stream.Position = position;
			}
		}
		#endregion
		#region DecodeImageOrientationOverride
		/// <summary>
		/// 解碼影像的方向。
		/// </summary>
		/// <param name="version">目前的工作版本。</param>
		/// <param name="stream">提供影像資料的資料流。</param>
		/// <returns>影像的方向。</returns>
		internal virtual ImageOrientation DecodeImageOrientationOverride(TaskVersion version, Stream stream)
		{
			return ImageOrientation.Rotate0;
		}
		#endregion
		#region DecodeMetadataInternal
		IMetadata DecodeMetadataInternal(Stream stream, bool isReadOnly)
		{
			long position = stream.Position;
			try
			{
				return this.DecodeMetadataOverride(stream, isReadOnly);
			}
			finally
			{
				stream.Position = position;
			}
		}
		#endregion
		#region DecodeMetadataOverride
		/// <summary>
		/// 解碼中繼資料。
		/// </summary>
		/// <param name="stream">提供影像資料的資料流。</param>
		/// <param name="isReadOnly">是否解碼成唯讀中繼資料。</param>
		/// <returns>影像中繼資料。</returns>
		internal virtual IMetadata DecodeMetadataOverride(Stream stream, bool isReadOnly)
		{
			return null;
		}
		#endregion
		#region DecodePixelSize
		/// <summary>
		/// 解碼影像像素大小。
		/// </summary>
		/// <param name="stream">提供影像資料的資料流。</param>
		/// <returns>影像像素大小。</returns>
		internal abstract Size<int> DecodePixelSize(Stream stream);
		#endregion
		#region DecodePixelSizeInternal
		Size<int> DecodePixelSizeInternal(Stream stream, bool rotate)
		{
			long position = stream.Position;
			try
			{
				Size<int> pixelSize = this.DecodePixelSize(stream);
				if (rotate && !pixelSize.IsEmpty)
				{
					int height = pixelSize.Width;
					pixelSize.Width = pixelSize.Height;
					pixelSize.Height = height;
				}
				return pixelSize;
			}
			finally
			{
				stream.Position = position;
			}
		}
		#endregion
		#region DecodeThumbnailInternal
		IImage DecodeThumbnailInternal(Stream stream)
		{
			long position = stream.Position;
			try
			{
				return this.DecodeThumbnailOverride(stream);
			}
			finally
			{
				stream.Position = position;
			}
		}
		#endregion
		#region DecodeThumbnailOverride
		/// <summary>
		/// 解碼內嵌縮圖。
		/// </summary>
		/// <param name="stream">提供影像資料的資料流。</param>
		/// <returns>內嵌縮圖，Null (Visual Basic 為 Nothing) 表示無內嵌縮圖。</returns>
		internal virtual IImage DecodeThumbnailOverride(Stream stream)
		{
			return null;
		}
		#endregion
	}
}
